package cn.xianshu.service.impl;

import cn.xianshu.constants.RedisConstants;
import cn.xianshu.dto.SendSmsDto;
import cn.xianshu.exception.GlobalException;
import cn.xianshu.service.IVerifyCodeService;
import cn.xianshu.to.SendVerifyCodeTo;
import cn.xianshu.util.AssertUtil;
import cn.xianshu.util.StrUtils;
import cn.xianshu.util.VerifyCodeUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.concurrent.TimeUnit;


@Service
@Slf4j
public class VerifyCodeServiceImpl implements IVerifyCodeService {

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;


    /**
     * 图片验证
     * 3.后端收到请求，获取到KEY，生成图片验证码的值
     * 4.把图片验证码的值存储到Redis，以前段传入的key作为Redis的key
     * 5.把图片验证码的值合并到一个图片中
     * 6.把图片基于Base64编码层字符串，响应给前端
     * @param key
     * @return
     */
    @Override
    public String imageCode(String key) {
        // 校验参数
        AssertUtil.isNotEmpty(key, "无效请求!");
        // 3.后端收到请求，获取到KEY，生成图片验证码的值
        String imageCode = StrUtils.getComplexRandomString(4);
        // 4.把图片验证码的值存储到Redis，以前段传入的key作为Redis的key
        redisTemplate.opsForValue().set(key, imageCode, 10L, TimeUnit.MINUTES);
        // 5.把图片验证码的值合并到一个图片中
        // 6.把图片基于Base64编码层字符串，响应给前端
        try {
            return VerifyCodeUtils.verifyCode(130, 35, imageCode);
        } catch (Exception e) {
            e.printStackTrace();
            throw new GlobalException("图片验证码生成失败!");
        }
    }


    /**
     * 手机验证码
     * 1.获取参数
     * 2.后台接收到请求，判断图片验证码是否合法
     * 3.判断上一次是否有一个未使用的有效验证码
     * 4.如果有，并且过了重发时间(两次发送时间大于60S)使用上一次有效验证码
     * 5.如果没有，创建一个新的短信验证码
     * 6.把短信验证码存储到Redis,格式如： SMS:18244229575={code:”1234” , sendTime: “mills”}
     * 7.调用第三方短信网关发送短信验证码
     * 8.DB存储验证码发送记录作为结算依据
     * @param dto
     */
    @Override
    public void sendSmsCode(SendSmsDto dto) {
        // 1.获取参数
        String imageCode = dto.getImageCode(); // 验证码
        String mobile = dto.getMobile(); // 电话号码
        String imageCodeKey = dto.getImageCodeKey(); // key
        AssertUtil.isNotEmpty(mobile, "手机号码不能为空!");
        AssertUtil.isNotEmpty(imageCodeKey, "非法请求!");
        AssertUtil.isNotEmpty(imageCode, "验证码不能为空!");


        // 2.后台接收到请求，判断图片验证码是否合法
        // 2.1 拿到传入的Key去redis获取图片验证码
        String redisKey = redisTemplate.opsForValue().get(imageCodeKey).toString();
        AssertUtil.isNotEmpty(redisKey, "无效的图片验码，请重新获取!");
        // 2.2 将传入的图片验证码和redis中的图片验证码比较
        AssertUtil.isStrEqual(imageCode,  redisKey, "验证码不正确，请重新输入!");

        String code = null;
        // 3.判断上一次是否有一个未使用的有效验证码
        String key = String.format(RedisConstants.KEY_SMS_CODE, mobile);
        // 3.1 从redis中获取上一次发送记录
        SendVerifyCodeTo lastSend = (SendVerifyCodeTo)redisTemplate.opsForValue().get(key);
        // 系统时间
        long nowTime = new Date().getTime();
        if (null != lastSend){
            // 3.2 判断当前时间是否比上一次发送时间大于60
            Boolean canSend = (nowTime - lastSend.getSendTime()) / 1000 > 10;
            AssertUtil.isTrue(canSend, "发送频繁，请稍后重试!");
            code = lastSend.getImageCode();
        } else {
            // 4.如果有，并且过了重发时间(两次发送时间大于60S)使用上一次有效验证码
            code = StrUtils.getComplexRandomString(6);
        }
        // 5.通过MQ异步发送验证码
        log.info("您的短信验证码为{}，请在{}分钟之内使用!", code, 3);
        // 6.把短信验证码存储到Redis,格式如： SMS:18244229575={code:”1234” , sendTime: “mills”}
        redisTemplate.opsForValue().set(key, new SendVerifyCodeTo(code, nowTime));
        // 7.存储一个发送记录到数据库（ID，手机号，时间，验证码，KEY，IP，类型）
        // 8.从Redis删除图片验证码
        redisTemplate.delete(imageCodeKey);
    }


}
